//==================================================================
//  Copyright (C) 2006  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
//==
//==
//==
//==================================================================

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "psys.h"
#include "m5d_tvert.h"
#include "m5d_mesh.h"

//==================================================================
using namespace M5D;
using namespace PSYSGFX;

//==================================================================
// $$$ but actually... position welding should be based also on weight and bone !
int Mesh::weld_do_weld_pos( int start_idx, int nv, std::vector<int> &idx_trans_table, float tollerance )
{
float	tollerance_sqr = tollerance * tollerance;
int		n_remapped;

	int	end_idx = start_idx + nv;

	n_remapped = 0;
	//------------------------------------------------------------------------
	const Vector3	*v1p = _geomp->GetVerts().GetPosW() + start_idx;
	for (int i=start_idx; i < end_idx; ++i, ++v1p)
	{
		//if NOT( tvg1p->flags & TriList::FLG_OPEN_EDGE )
		//	continue;
		const Vector3	*v2p = v1p + 1;
		for (int j=i+1; j < end_idx; ++j, ++v2p)
		{
			//if NOT( tvg2p->flags & TriList::FLG_OPEN_EDGE )
			//	continue;

			Vector3	vtmp = *v1p - *v2p;
			vtmp = vtmp * vtmp;

			// replace i with j, this way ruined vertices never come back to be parsed !
			//if ( x*x + y*y + z*z <= tollerance_sqr )
			if ( vtmp._x <= tollerance_sqr && vtmp._y <= tollerance_sqr && vtmp._z <= tollerance_sqr )
			{
				idx_trans_table[i] = j;
				n_remapped += 1;
			}
		}
	}

	return n_remapped;
}
//==================================================================
int Mesh::weld_do_weld_tex( int start_idx, int nv, std::vector<int> &idx_trans_table, float tollerance )
{
int		i, j;
Vector2	*v1p, *v2p;
float	tollerance_sqr = tollerance * tollerance;
int		n_remapped;

	int	end_idx = start_idx + nv;

	n_remapped = 0;
	//------------------------------------------------------------------------
	v1p = _geomp->GetVerts().GetTexW() + start_idx;
	for (i=start_idx; i < end_idx; ++i, ++v1p)
	{
		//if NOT( tvg1p->flags & TriList::FLG_OPEN_EDGE )
		//	continue;

		v2p = v1p + 1;
		for (j=i+1; j < end_idx; ++j, ++v2p)
		{
			//if NOT( tvg2p->flags & TriList::FLG_OPEN_EDGE )
			//	continue;

			Vector2	tmp = (*v1p - *v2p).GetSquared();

			// replace i with j, this way ruined vertices never come back to be parsed !
			//if ( x*x + y*y <= tollerance_sqr )
			//if ( x <= tollerance_sqr && y <= tollerance_sqr )
			if ( tmp._x <= tollerance_sqr && tmp._y <= tollerance_sqr )
			{
				idx_trans_table[i] = j;
				n_remapped += 1;
			}
		}
	}

	return n_remapped;
}
//==================================================================
int Mesh::weld_do_weld_col( int start_idx, int nv, std::vector<int> &idx_trans_table, float tollerance )
{
int		i, j;
Color4b	*v1p, *v2p;
float	x, y, z, w;
float	tollerance_sqr = tollerance * tollerance;
int		n_remapped;

	int	end_idx = start_idx + nv;

	n_remapped = 0;
	//------------------------------------------------------------------------
	v1p = _geomp->GetVerts().GetColW() + start_idx;
	for (i=start_idx; i < end_idx; ++i, ++v1p)
	{
		//if NOT( tvg1p->flags & TriList::FLG_OPEN_EDGE )
		//	continue;
		float	v1x = v1p->_a;
		float	v1y = v1p->_r;
		float	v1z = v1p->_g;
		float	v1w = v1p->_b;

		v2p = v1p + 1;
		for (j=i+1; j < end_idx; ++j, ++v2p)
		{
			//if NOT( tvg2p->flags & TriList::FLG_OPEN_EDGE )
			//	continue;

			x = v1x - (float)v2p->_a; x *= x;
			y = v1y - (float)v2p->_r; y *= y;
			z = v1z - (float)v2p->_g; z *= z;
			w = v1w - (float)v2p->_b; w *= w;

			// replace i with j, this way ruined vertices never come back to be parsed !
			//if ( x*x + y*y + z*z + w*w <= tollerance_sqr )
			if ( x <= tollerance_sqr && y <= tollerance_sqr && z <= tollerance_sqr && w <= tollerance_sqr )
			{
				idx_trans_table[i] = j;
				n_remapped += 1;
			}
		}
	}

	return n_remapped;
}
//==================================================================
int Mesh::weld_do_weld_nor( int start_idx, int nv, std::vector<int> &idx_trans_table, float tollerance )
{
int		i, j;
Vector3	*v1p, *v2p;
float	tollerance_sqr = tollerance * tollerance;
int		n_remapped;

	int	end_idx = start_idx + nv;

	n_remapped = 0;
	//------------------------------------------------------------------------
	v1p = _geomp->GetVerts().GetNorW() + start_idx;
	for (i=start_idx; i < end_idx; ++i, ++v1p)
	{
		Vector3	v1tmp = *v1p;

		v2p = v1p + 1;
		for (j=i+1; j < end_idx; ++j, ++v2p)
		{
			Vector3	dist = (v1tmp - *v2p).GetSquared();

			// replace i with j, this way ruined vertices never come back to be parsed !
			//if ( x*x + y*y + z*z <= tollerance_sqr )
			if ( dist._x <= tollerance_sqr && dist._y <= tollerance_sqr && dist._z <= tollerance_sqr )
			{
				idx_trans_table[i] = j;
				n_remapped += 1;
			}
		}
	}

	return n_remapped;
}

//==================================================================
void	Mesh::msh_weld( VType::VType type_idx, float tollerance, int from_vert_idx )
{
	int	i, nv;

	_geomp->GetVerts().LockRW( __FILE__, __LINE__ );

	switch ( type_idx )
	{
	case VType::POS:	nv = _geomp->GetVerts().GetPosN(); break;
	case VType::TEX:	nv = _geomp->GetVerts().GetTexN(); break;
	case VType::COL:	nv = _geomp->GetVerts().GetColN(); break;
	case VType::NOR:	nv = _geomp->GetVerts().GetNorN(); break;
	default:
		PASSERT( 0 );
		_geomp->GetVerts().Unlock();
		throw "Wrong vertex type ?!";
		break;
	}
	if NOT( nv >= 2 )
		return;

	std::vector<int>	vert_remap_table( nv );

	for (i=0; i < nv; ++i)
		vert_remap_table[i] = i;

	int n_remapped = 0;
	int	n;

	switch ( type_idx )
	{
	case VType::POS:
		for (i=from_vert_idx; i < nv; i += 5000)
		{
			n = 5000;
			if ( i + n > nv )
				n = nv - i;
			n_remapped += weld_do_weld_pos( i, n, vert_remap_table, tollerance );
		}
		break;
	case VType::TEX:
		for (i=from_vert_idx; i < nv; i += 5000)
		{
			n = 5000;
			if ( i + n > nv )
				n = nv - i;
			n_remapped += weld_do_weld_tex( i, n, vert_remap_table, tollerance );
		}
		break;
	case VType::COL:
		for (i=from_vert_idx; i < nv; i += 5000)
		{
			n = 5000;
			if ( i + n > nv )
				n = nv - i;
			n_remapped += weld_do_weld_col( i, n, vert_remap_table, tollerance );
		}
		break;
	case VType::NOR:
		for (i=from_vert_idx; i < nv; i += 5000)
		{
			n = 5000;
			if ( i + n > nv )
				n = nv - i;
			n_remapped += weld_do_weld_nor( i, n, vert_remap_table, tollerance );
		}
		break;
	}

	if ( n_remapped )
	{
		remapVertsInTriangles( type_idx, vert_remap_table );
		eliminateUnusedVerts( type_idx );
	}

	_geomp->GetVerts().Unlock();
}
